﻿// MIT License
// 开源地址：https://gitee.com/co1024/AbcMvc
// Copyright (c) 2021-2023 1024
// Abc.Mvc=Furion+EF+SqlSugar+Pear layui admin.

using Furion.EventBus;
using Newtonsoft.Json;
using System.Threading.Channels;

namespace Abc.Core.EventBus.Storers.Kafka;

/// <summary>
/// Kafka 存储源
/// </summary>
public class KafkaEventSourceStore : IEventSourceStorer, IDisposable
{
    /// <summary>
    /// 内存通道事件源存储器
    /// </summary>
    private readonly Channel<IEventSource> _channel;
    /// <summary>
    /// 主题
    /// </summary>
    private readonly string _topic;
    /// <summary>
    /// 消费者
    /// </summary>
    private readonly EventConsumer<Null, string> _eventConsumer;
    /// <summary>
    /// 生产者
    /// </summary>
    private readonly IProducer<Null, string> _producer;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="consumerConf">消费者配置</param>
    /// <param name="producerConf">生产者配置</param>
    /// <param name="topic">主题</param>
    /// <param name="capacity">存储器最多能够处理多少消息，超过该容量进入等待写入</param>
    public KafkaEventSourceStore(ConsumerConfig consumerConf, ProducerConfig producerConf, string topic, int capacity)
    {
        // 配置通道，设置超出默认容量后进入等待
        var boundedChannelOptions = new BoundedChannelOptions(capacity)
        {
            FullMode = BoundedChannelFullMode.Wait
        };

        // 创建有限容量通道
        _channel = Channel.CreateBounded<IEventSource>(boundedChannelOptions);

        // 主题
        _topic = topic;

        // 创建消息订阅者
        _eventConsumer = new EventConsumer<Null, string>(consumerConf);
        _eventConsumer.Consumer.Subscribe(new[] { topic });

        // 订阅消息写入 Channel
        _eventConsumer.Received += (send, cr) =>
        {
            // 反序列化消息
            var eventSource = JsonConvert.DeserializeObject<ChannelEventSource>(cr.Message.Value);

            // 写入内存管道存储器
            _channel.Writer.WriteAsync(eventSource);
        };

        // 启动消费者
        _eventConsumer.Start();

        // 创建生产者
        _producer = new ProducerBuilder<Null, string>(producerConf).Build();
    }

    /// <summary>
    /// 将事件源写入存储器
    /// </summary>
    /// <param name="eventSource">事件源对象</param>
    /// <param name="cancellationToken">取消任务 Token</param>
    /// <returns><see cref="ValueTask"/></returns>
    public async ValueTask WriteAsync(IEventSource eventSource, CancellationToken cancellationToken)
    {
        if (eventSource == default)
        {
            throw new ArgumentNullException(nameof(eventSource));
        }
        // 这里判断是否是 ChannelEventSource 或者 自定义的 EventSource
        if (eventSource is ChannelEventSource source)
        {
            // 序列化
            var data = JsonConvert.SerializeObject(source);
            // 异步发布
            await _producer.ProduceAsync(_topic, new Message<Null, string>
            {
                Value = data
            }, cancellationToken);
        }
        else
        {
            // 这里处理动态订阅问题
            await _channel.Writer.WriteAsync(eventSource, cancellationToken);
        }
    }

    /// <summary>
    /// 从存储器中读取一条事件源
    /// </summary>
    /// <param name="cancellationToken">取消任务 Token</param>
    /// <returns>事件源对象</returns>
    public async ValueTask<IEventSource> ReadAsync(CancellationToken cancellationToken)
    {
        // 读取一条事件源
        var eventSource = await _channel.Reader.ReadAsync(cancellationToken);
        return eventSource;
    }

    /// <summary>
    /// 释放非托管资源
    /// </summary>
    public async void Dispose()
    {
        await _eventConsumer.Stop();
        GC.SuppressFinalize(this);
    }
}